<template>
  <div class="notes-page">
    <!-- 最外层禁止滚动：overflow:hidden -->
    <el-container class="outer-container">
      <!-- 左侧可收缩文件列表 -->
      <el-aside
        :width="isListCollapsed ? '48px' : '240px'"
        class="notes-aside"
        :class="{ collapsed: isListCollapsed }"
      >
        <!-- 折叠/展开按钮 -->
        <div class="aside-toggle" @click="isListCollapsed = !isListCollapsed">
          <i :class="isListCollapsed ? 'el-icon-arrow-right' : 'el-icon-arrow-left'"></i>
        </div>

        <!-- 筛选 & 搜索 按钮，只在展开时显示 -->
        <div class="aside-tools" v-if="!isListCollapsed">
          <!-- 筛选弹窗 -->
          <el-popover
            placement="bottom"
            width="260"
            v-model="filterVisible"
            trigger="click"
          >
            <div class="filter-form">
              <el-form :model="filterForm" label-width="60px" size="mini">
                <el-form-item label="类型">
                  <el-select v-model="filterForm.type" placeholder="请选择">
                    <el-option label="PDF" value="pdf" />
                    <el-option label="DOCX" value="docx" />
                  </el-select>
                </el-form-item>
                <el-form-item label="状态">
                  <el-select v-model="filterForm.status" placeholder="请选择">
                    <el-option label="解析成功" value="success" />
                    <el-option label="解析失败" value="error" />
                  </el-select>
                </el-form-item>
                <el-form-item label="日期">
                  <el-date-picker
                    v-model="filterForm.range"
                    type="daterange"
                    range-separator="至"
                    start-placeholder="开始日期"
                    end-placeholder="结束日期"
                    style="width: 100%;"
                  />
                </el-form-item>
                <div class="tools-btns">
                  <el-button plain size="mini" @click="resetFilter">重置</el-button>
                  <el-button type="primary" size="mini" @click="applyFilter">确定</el-button>
                </div>
              </el-form>
            </div>
            <!-- 触发按钮 -->
            <el-button icon="el-icon-s-operation" circle slot="reference" />
          </el-popover>

          <!-- 搜索弹窗 -->
          <el-popover
            placement="bottom"
            width="220"
            v-model="searchVisible"
            trigger="click"
          >
            <div class="search-pop">
              <el-input
                v-model="searchText"
                size="mini"
                placeholder="请输入文件名称"
                @keyup.enter="applySearch"
              />
              <div class="search-actions">
                <el-button type="text" size="mini" @click="cancelSearch">取消</el-button>
                <el-button type="primary" size="mini" @click="applySearch">搜索</el-button>
              </div>
            </div>
            <!-- 触发按钮 -->
            <el-button icon="el-icon-search" circle slot="reference" />
          </el-popover>
        </div>

        <!-- 文件列表：去掉 router 属性，使用 @select 来手动切换路由 -->
        <el-menu
          class="notes-menu"
          :collapse="isListCollapsed"
          @select="onFileSelect"
          :default-active="currentFileId"
        >
          <el-menu-item
            v-for="f in filteredList"
            :key="f.id"
            :index="f.id"
            class="pdf-menu-item"
          >
            <img 
              v-if="f.type === 'userPdf'" 
              src="@/assets/icon/pdf-red.png" 
              class="pdf-icon"
              alt="PDF"
            />
            <i v-else :class="iconMap[f.type]"></i>
            <span slot="title" class="pdf-filename">{{ f.name }}</span>
            <!-- 状态徽章 -->
            <el-badge
              class="status-badge"
              :value="statusMap[f.status].text"
              :type="statusMap[f.status].type"
            />
          </el-menu-item>
        </el-menu>
      </el-aside>

      <!-- 右侧主内容区 -->
      <el-container class="right-container">
        <!-- 顶部操作栏 -->
        <el-header class="notes-header">
          <!-- 左侧显示当前文件名 -->
          <div class="header-left">{{ currentFile?.name || '未选中文件' }}</div>

          <!-- 中间切换标签 -->
          <div class="header-center">
            <div 
              class="tab-item"
              :class="{ active: selectedPanes.includes('pdf') }"
              @click="togglePane('pdf')"
            >
              <i class="el-icon-document"></i>
              <span>PDF预览</span>
            </div>
            <div 
              class="tab-item"
              :class="{ active: selectedPanes.includes('markdown') }"
              @click="togglePane('markdown')"
            >
              <i class="el-icon-edit-outline"></i>
              <span>Markdown提取</span>
            </div>
            <div 
              class="tab-item"
              :class="{ active: selectedPanes.includes('chat') }"
              @click="togglePane('chat')"
            >
              <i class="el-icon-chat-dot-round"></i>
              <span>AI对话</span>
            </div>
          </div>

          <!-- 右侧分享 / 下载 -->
          <div class="header-right">
            <el-button icon="el-icon-share" circle @click="onShare" />
            <el-button 
              icon="el-icon-download" 
              circle 
              @click="onDownload"
              :loading="isDownloading"
              :disabled="!currentFile || isDownloading"
              :title="!currentFile ? '请先选择文件' : '下载完整论文包'"
            />
            <el-button icon="el-icon-refresh" circle @click="testMarkdownRender" title="测试Markdown渲染" />
            <el-button icon="el-icon-chat-line-round" circle @click="testAIResponseFormatting" title="测试AI回复格式化" />
            <el-button icon="el-icon-s-operation" circle @click="debugLatexProcessing" title="调试LaTeX处理" />
          </div>
        </el-header>

        <!-- 动态两栏主区域 -->
        <el-main class="notes-main">
          <!-- 【原文 Pane】 -->
          <div
            v-show="selectedPanes.includes('pdf')"
            class="pane"
            :class="paneClasses.pdf"
          >
            <div class="pane-inner">
              <template v-if="currentPdfUrl">
                <!-- PDF缩放控制栏 -->
                <div class="pdf-controls">
                  <el-button-group>
                    <el-button 
                      size="mini" 
                      icon="el-icon-zoom-out" 
                      @click="zoomOut"
                      :disabled="pdfZoom <= 0.5"
                    ></el-button>
                    <el-button size="mini">{{ Math.round(pdfZoom * 100) }}%</el-button>
                    <el-button 
                      size="mini" 
                      icon="el-icon-zoom-in" 
                      @click="zoomIn"
                      :disabled="pdfZoom >= 3"
                    ></el-button>
                    <el-button 
                      size="mini" 
                      @click="resetZoom"
                    >重置</el-button>
                  </el-button-group>
                  
                  <!-- PDF页面导航 -->
                  <el-button-group v-if="pdfTotalPages > 1" style="margin-left: 16px;">
                    <el-button 
                      size="mini" 
                      icon="el-icon-arrow-left" 
                      @click="prevPdfPage"
                      :disabled="currentPdfPage <= 1"
                    ></el-button>
                    <el-button size="mini">{{ currentPdfPage }} / {{ pdfTotalPages }}</el-button>
                    <el-button 
                      size="mini" 
                      icon="el-icon-arrow-right" 
                      @click="nextPdfPage"
                      :disabled="currentPdfPage >= pdfTotalPages"
                    ></el-button>
                  </el-button-group>
                </div>
                
                <!-- PDF预览容器 -->
                <div class="pdf-container" ref="pdfContainer">
                  <!-- 使用自定义PDF查看器组件 -->
                  <PdfViewer
                    :url="currentPdfUrl"
                    :zoom="pdfZoom"
                    :page="currentPdfPage"
                    @loaded="onPdfLoaded"
                    @error="onPdfError"
                    @pageRendered="onPdfPageRendered"
                    @pageChanged="onPdfPageChanged"
                  />
                </div>
              </template>
              <template v-else>
                <div class="placeholder">未选中文件或预览地址无效</div>
              </template>
            </div>
          </div>

          <!-- 【提取 Pane】 -->
          <div
            v-show="selectedPanes.includes('markdown')"
            class="pane"
            :class="paneClasses.markdown"
          >
            <div class="pane-inner">
              <template v-if="markdownLoading">
                <div class="loading-container">
                  <i class="el-icon-loading"></i>
                  <p>正在加载Markdown文件...</p>
                </div>
              </template>
              <template v-else-if="currentMarkdownContent">
                <div class="markdown-content" v-html="renderedMarkdown"></div>
              </template>
              <template v-else>
                <div class="placeholder">未找到对应的Markdown文件</div>
              </template>
            </div>
          </div>

          <!-- 【对话 Pane】 -->
          <div
            v-show="selectedPanes.includes('chat')"
            class="pane chat-pane"
            :class="paneClasses.chat"
          >
            <div class="chat-layout">
              <!-- AI上下文提示 -->
              <div class="ai-context-tip" v-if="aiContext" @contextmenu.prevent="showContextMenu">
                <i class="el-icon-info"></i>
                <span class="context-text">{{ getContextSummary() }}</span>
                <el-button 
                  type="text" 
                  size="mini" 
                  @click="setAIContext"
                  class="edit-context-btn"
                >
                  <i class="el-icon-edit"></i>
                </el-button>
              </div>
              
              <!-- 聊天消息区（独立滚动） -->
              <div class="chat-container" ref="chatBox">
                <div
                  v-for="(item, index) in chatHistory"
                  :key="index"
                  :class="['chat-item', item.type]"
                >
                  <div class="chat-bubble">
                    <template v-if="item.loading">
                      <i class="el-icon-loading"></i> 思考中...
                    </template>
                    <template v-else>
                      <!-- AI消息需要特殊处理 -->
                      <div v-if="item.type === 'ai'" class="ai-message" v-html="formattedAIResponse(item.displayedText, item.updateTime)"></div>
                      <!-- 用户消息直接显示 -->
                      <div v-else>{{ item.displayedText }}</div>
                    </template>
                  </div>
                </div>
              </div>
              <!-- 聊天输入区：固定在对话 Pane 底部 -->
              <div class="chat-input-area-inside">
                <el-button
                  type="text"
                  icon="el-icon-plus"
                  @click="startNewConversation"
                  title="新建对话"
                  class="new-chat-btn"
                >新建</el-button>
                <el-button
                  type="text"
                  icon="el-icon-setting"
                  @click="setAIContext"
                  title="设置AI上下文"
                  class="context-btn"
                >设置</el-button>
                <el-button
                  type="text"
                  icon="el-icon-refresh"
                  @click="testAIResponseFormatting"
                  title="测试AI回复格式化"
                  class="test-btn"
                >测试</el-button>
                <el-input
                  type="textarea"
                  :autosize="{ minRows: 1, maxRows: 4 }"
                  v-model="currentQuestion"
                  placeholder="请输入你的问题..."
                  @keyup.enter.native="sendQuestion"
                  show-word-limit
                  maxlength="500"
                  class="input-field-textarea"
                />
                <el-button
                  type="primary"
                  class="send-button"
                  @click="sendQuestion"
                  >发送</el-button
                >
              </div>
            </div>
          </div>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script>
// 条件导入Markdown渲染库
let marked = null;
let katex = null;
let hljs = null;

try {
  // 修复marked导入方式
  const markedModule = require('marked');
  marked = markedModule.marked || markedModule.default || markedModule;
  
  katex = require('katex');
  hljs = require('highlight.js');
  
  // 导入样式
  require('katex/dist/katex.min.css');
  require('highlight.js/styles/github.css');
} catch (error) {
  console.warn('Markdown渲染库未安装，将使用基础渲染:', error.message);
}

// 导入PDF查看器组件
import PdfViewer from '@/components/PdfViewer.vue';
import { getCurrentUserId } from '@/utils/auth.js';

export default {
  name: "NotesPage",
  components: {
    PdfViewer
  },
  data() {
    return {
      // — 左侧文件列表收缩状态 —
      isListCollapsed: false,

      // — 筛选 & 搜索 —
      filterVisible: false,
      searchVisible: false,
      searchText: "",
      filterForm: { type: "", status: "", range: [] },

      // — 选中的栏位数组 —
      selectedPanes: ['pdf'],

      // — 模拟后端返回的文件列表（可替换成真实接口） —
      files: [],

      // — 对话区相关 —
      chatHistory: [],
      currentQuestion: "",
      isSaving: false,
      savingTimer: null,
      currentChatId: null, // 当前聊天会话ID
      
      // AI对话预设上下文
      aiContext: "假设你是一位科研工作者，你正在研读一篇学术论文，我可能有一些专业知识需要你解释，或者我会让你翻译一段markdown文本，注意其中有一些latex公式也需要嵌入在最终的翻译结果中。请用中文回答。\n\n",

      // — PDF和Markdown文件相关 —
      currentPdfUrl: "",
      currentMarkdownContent: "",
      markdownLoading: false,
      pdfZoom: 1, // PDF缩放比例
      currentPdfPage: 1,
      pdfTotalPages: 1,
      
      // — 下载状态 —
      isDownloading: false,
    };
  },
  computed: {
    // 当前路由中的 fileId（?fileId=xxx）
    currentFileId() {
      return this.$route.query.fileId || "";
    },
    // 根据 fileId 找出当前文件对象
    currentFile() {
      return this.files.find((f) => f.id === this.currentFileId);
    },
    // 图标映射
    iconMap() {
      return {
        pdf: "el-icon-document",
        docx: "el-icon-document",
        pptx: "el-icon-document",
        userPdf: "el-icon-document",      // 用户PDF文件
      };
    },
    // 状态对应文案 + Badge 类型
    statusMap() {
      return {
        success: { text: "成功", type: "success" },
        error: { text: "失败", type: "danger" },
        parsing: { text: "解析中", type: "warning" },
      };
    },
    // 先搜索，再筛选，输出最终列表
    filteredList() {
      return this.files.filter((f) => {
        // 搜索名字
        if (this.searchText && !f.name.includes(this.searchText)) return false;
        // 类型筛选
        if (this.filterForm.type && f.type !== this.filterForm.type) return false;
        // 状态筛选
        if (this.filterForm.status && f.status !== this.filterForm.status)
          return false;
        // 日期范围筛选
        if (this.filterForm.range.length === 2) {
          const t = new Date(f.createdAt).getTime();
          const [s, e] = this.filterForm.range.map((d) => new Date(d).getTime());
          if (t < s || t > e) return false;
        }
        return true;
      });
    },
    // 获取栏位的CSS类
    paneClasses() {
      return {
        pdf: {
          'left-pane': this.selectedPanes.indexOf('pdf') === 0,
          'right-pane': this.selectedPanes.indexOf('pdf') === 1,
          'single-pane': this.selectedPanes.length === 1 && this.selectedPanes.includes('pdf')
        },
        markdown: {
          'left-pane': this.selectedPanes.indexOf('markdown') === 0,
          'right-pane': this.selectedPanes.indexOf('markdown') === 1,
          'single-pane': this.selectedPanes.length === 1 && this.selectedPanes.includes('markdown')
        },
        chat: {
          'left-pane': this.selectedPanes.indexOf('chat') === 0,
          'right-pane': this.selectedPanes.indexOf('chat') === 1,
          'single-pane': this.selectedPanes.length === 1 && this.selectedPanes.includes('chat')
        }
      };
    },
    // 渲染Markdown内容
    renderedMarkdown() {
      if (!this.currentMarkdownContent) return '';
      
      try {
        // 如果没有安装marked库，使用基础渲染
        if (!marked) {
          return this.basicMarkdownRender(this.currentMarkdownContent);
        }

        // 配置marked选项
        marked.setOptions({
          highlight: function(code, lang) {
            if (lang && hljs && hljs.getLanguage(lang)) {
              try {
                return hljs.highlight(code, { language: lang }).value;
              } catch (err) {}
            }
            return code;
          },
          breaks: true,
          gfm: true
        });

        // 处理图片链接，将相对路径转换为绝对路径
        let processedContent = this.currentMarkdownContent;
        
        // 处理图片链接：将 ![](images/xxx.jpg) 转换为完整的URL
        processedContent = processedContent.replace(
          /!\[([^\]]*)\]\(images\/([^)]+)\)/g,
          (match, alt, filename) => {
            const fileId = this.currentFile?.id || '';
            if (!fileId) {
              return `<div style="color: red; background: #ffe6e6; padding: 8px; border: 1px solid #ffcccc; margin: 8px 0;">
                <strong>图片加载失败:</strong> ${filename}<br>
                <small>原因: 无法获取文件ID</small>
              </div>`;
            }
            const imageUrl = `/file/previewImage?fileId=${fileId}&imageName=${filename}`;
            return `<img src="${imageUrl}" alt="${alt}" style="max-width: 100%; height: auto;">`;
          }
        );

        // 处理数学公式 - 改进处理方式
        if (katex) {
          // 处理块级数学公式 $$...$$
          processedContent = processedContent.replace(/\$\$([\s\S]*?)\$\$/g, (match, formula) => {
            try {
              console.log('处理块级公式:', formula);
              return katex.renderToString(formula.trim(), { 
                displayMode: true,
                throwOnError: false,
                errorColor: '#cc0000'
              });
            } catch (e) {
              console.error('块级公式渲染失败:', e);
              return `<div class="math-error">公式渲染失败: ${formula}</div>`;
            }
          });

          // 处理行内数学公式 $...$
          processedContent = processedContent.replace(/\$([^$\n]+?)\$/g, (match, formula) => {
            try {
              console.log('处理行内公式:', formula);
              return katex.renderToString(formula.trim(), { 
                displayMode: false,
                throwOnError: false,
                errorColor: '#cc0000'
              });
            } catch (e) {
              console.error('行内公式渲染失败:', e);
              return `<span class="math-error">公式渲染失败: ${formula}</span>`;
            }
          });
        }

        const result = marked(processedContent);
        console.log('Markdown渲染结果:', result);
        return result;
      } catch (error) {
        console.error('Markdown渲染错误:', error);
        return this.basicMarkdownRender(this.currentMarkdownContent);
      }
    },
    
    // 格式化AI消息显示
    formattedAIResponse() {
      return (text, updateTime) => {
        if (!text) return '';
        // 使用updateTime确保每次更新都重新计算
        return this.formatAIResponse(text);
      };
    },
  },
  methods: {
    // 基础Markdown渲染（fallback）
    basicMarkdownRender(content) {
      if (!content) return '';
      
      let html = content
        // 处理标题
        .replace(/^### (.*$)/gim, '<h3>$1</h3>')
        .replace(/^## (.*$)/gim, '<h2>$1</h2>')
        .replace(/^# (.*$)/gim, '<h1>$1</h1>')
        // 处理粗体和斜体
        .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
        .replace(/\*(.*?)\*/g, '<em>$1</em>')
        // 处理代码块
        .replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>')
        .replace(/`([^`]+)`/g, '<code>$1</code>')
        // 处理链接
        .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>')
        // 处理图片
        .replace(/!\[([^\]]*)\]\(images\/([^)]+)\)/g, (match, alt, filename) => {
          const baseUrl = this.currentFile?.preview_url || '';
          const imageUrl = baseUrl.replace(/\/[^\/]+\.pdf$/, `/images/${filename}`);
          return `<img src="${imageUrl}" alt="${alt}" style="max-width: 100%; height: auto;">`;
        })
        // 处理列表
        .replace(/^\* (.*$)/gim, '<li>$1</li>')
        .replace(/^\- (.*$)/gim, '<li>$1</li>')
        // 处理段落
        .replace(/\n\n/g, '</p><p>')
        .replace(/^(.+)$/gm, '<p>$1</p>');

      // 清理多余的p标签
      html = html.replace(/<p><\/p>/g, '');
      
      return html;
    },

    /**
     * 点击左侧文件项：
     * 1. 点击时先将 `mode` 设为 'original-extract'（始终切回三栏）；  
     * 2. 如果 id != currentFileId，则调用 `router.replace(...)` 替换 URL query；  
     * 3. 最后 `this.$nextTick()` 后，将所有 Pane 内的滚动都重置到顶部。  
     */
    onFileSelect(id) {
      this.selectedPanes = ['pdf'];

      if (id !== this.currentFileId) {
        // 切换文件时，清空当前对话并加载新PDF的对话
        this.chatHistory = [];
        this.currentChatId = null;
        this.currentQuestion = "";
        localStorage.removeItem("currentChatHistory");
        localStorage.removeItem("currentChatId");
        
        this.$router
          .replace({
            path: "/notes",
            query: { fileId: id },
          })
          .catch((e) => {
            // 忽略重复导航错误
            if (e.name !== "NavigationDuplicated") console.error(e);
          });
      }

      // 切换路由或模式后，将各 Pane 滚到顶部
      this.$nextTick(() => {
        const leftInner = this.$el.querySelector(".left-pane .pane-inner");
        const chatBox = this.$refs.chatBox;
        if (leftInner) leftInner.scrollTop = 0;
        if (chatBox) chatBox.scrollTop = 0;
      });
    },

    // 重置筛选表单
    resetFilter() {
      this.filterForm = { type: "", status: "", range: [] };
    },
    applyFilter() {
      this.filterVisible = false;
    },
    cancelSearch() {
      this.searchVisible = false;
    },
    applySearch() {
      this.searchVisible = false;
    },

    onShare() {
      this.$message.info("分享功能待实现");
    },
    onDownload() {
      if (!this.currentFile || !this.currentFile.id) {
        this.$message.warning('请先选择一个文件');
        return;
      }

      // 显示下载确认对话框
      this.$confirm(
        `确认下载 "${this.currentFile.name}" 的完整论文包吗？\n\n包含内容：\n• 原始PDF文件\n• Markdown提取文件\n• 图片文件\n• 说明文档`,
        '下载确认',
        {
          confirmButtonText: '开始下载',
          cancelButtonText: '取消',
          type: 'info',
          dangerouslyUseHTMLString: true
        }
      ).then(() => {
        this.startDownload();
      }).catch(() => {
        // 用户取消下载
      });
    },

    // 开始下载
    async startDownload() {
      try {
        this.isDownloading = true;
        this.$message.info('正在准备下载文件，请稍候...');
        
        // 构建下载URL
        const downloadUrl = `/file/downloadPaper/${this.currentFile.id}`;
        
        // 创建下载链接
        const link = document.createElement('a');
        link.href = downloadUrl;
        link.download = `${this.currentFile.name.replace('.pdf', '')}_完整论文.zip`;
        link.target = '_blank';
        
        // 添加到DOM并触发点击
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        
        this.$message.success('开始下载完整论文包');
        
        // 延迟显示成功消息并重置加载状态
        setTimeout(() => {
          this.isDownloading = false;
          this.$message.success('下载完成！文件包含PDF、Markdown和图片等完整内容');
        }, 2000);
        
      } catch (error) {
        console.error('下载失败:', error);
        this.isDownloading = false;
        this.$message.error('下载失败，请重试');
      }
    },

    // PDF缩放控制
    zoomOut() {
      if (this.pdfZoom > 0.5) {
        this.pdfZoom = Math.round((this.pdfZoom - 0.1) * 10) / 10;
      }
    },
    zoomIn() {
      if (this.pdfZoom < 3) {
        this.pdfZoom = Math.round((this.pdfZoom + 0.1) * 10) / 10;
      }
    },
    resetZoom() {
      this.pdfZoom = 1;
    },

    /*** 聊天区相关 ***/
    // 加载历史对话
    async loadChatHistory(chatId) {
      try {
        const response = await this.$request.get(`/api/chat/messages/${chatId}`);
        if (response.code === '200') {
          this.chatHistory = response.data.map(msg => ({
            text: msg.message,
            displayedText: msg.message,
            type: msg.sender === 'user' ? 'user' : 'ai',
            loading: false,
            updateTime: Date.now()
          }));
          this.currentChatId = chatId;
          this.$nextTick(() => this.scrollToBottom());
        } else {
          this.$message.error('加载历史对话失败: ' + response.msg);
        }
      } catch (error) {
        console.error('加载历史对话失败:', error);
        this.$message.error('加载历史对话失败: ' + error.message);
      }
    },

    // 新建对话：把当前对话存入历史，然后清空
    async startNewConversation() {
      if (this.chatHistory.length > 0) {
        // 如果有当前会话，更新状态
        if (this.currentChatId) {
          try {
            await this.$request.put(`/api/chat/session/${this.currentChatId}/state`, {
              state: 'archived'
            });
          } catch (error) {
            console.error('更新会话状态失败:', error);
          }
        }
        
        // 保存到本地存储作为备份
        const historyList = JSON.parse(
          localStorage.getItem("chatHistoryList") || "[]"
        );
        historyList.push({
          timestamp: new Date().toISOString(),
          chat: this.chatHistory,
          chatId: this.currentChatId
        });
        localStorage.setItem("chatHistoryList", JSON.stringify(historyList));
        this.$message.success("对话已保存到历史");
      }
      
      this.chatHistory = [];
      this.currentQuestion = "";
      this.currentChatId = null;
      
      // 清理localStorage中的当前会话信息
      localStorage.removeItem("currentChatHistory");
      localStorage.removeItem("currentChatId");
      
      this.$nextTick(() => this.scrollToBottom());
    },

    // 点击发送按钮或回车
    async sendQuestion() {
      if (!this.currentQuestion.trim()) {
        this.$message.warning("请输入问题");
        return;
      }
      
      const question = this.currentQuestion.trim();
      
      // 用户消息
      this.chatHistory.push({
        text: question,
        displayedText: question,
        type: "user",
        loading: false,
        updateTime: Date.now()
      });
      
      // AI 占位
      this.chatHistory.push({
        text: "",
        displayedText: "",
        type: "ai",
        loading: true,
        updateTime: Date.now()
      });
      
      this.currentQuestion = "";
      this.$nextTick(() => this.scrollToBottom());

      try {
        // 获取用户ID和当前文件ID
        const uid = parseInt(getCurrentUserId() || "1");
        const currentFileId = parseInt(this.currentFileId);
        
        if (!currentFileId) {
          throw new Error('未选中文件，无法发送消息');
        }
        
        // 如果是第一次对话，创建新的会话
        if (!this.currentChatId) {
          const sessionResponse = await this.$request.post('/api/chat/session', {
            uid: uid,
            title: question.substring(0, 50) + (question.length > 50 ? '...' : ''),
            pdfId: currentFileId
          });
          
          if (sessionResponse.code === '200') {
            this.currentChatId = sessionResponse.data.chatId;
            // 立即保存到localStorage
            localStorage.setItem("currentChatId", this.currentChatId.toString());
            console.log('创建新对话会话:', this.currentChatId, '对应PDF:', currentFileId);
          } else {
            throw new Error('创建会话失败: ' + sessionResponse.msg);
          }
        }

        // 发送消息到AI并接收流式回复
        await this.sendMessageToAI(question, uid);
        
      } catch (error) {
        console.error('发送消息失败:', error);
        this.$message.error('发送消息失败: ' + error.message);
        
        // 移除加载中的AI消息
        this.chatHistory.pop();
      }
      
      this.saveCurrentChat();
    },

    // 发送消息到AI并接收流式回复
    async sendMessageToAI(message, uid) {
      const aiMessageIndex = this.chatHistory.length - 1;
      let fullResponse = '';
      let eventSource = null;
      
      try {
        // 构建完整的消息，包含预设上下文
        const fullMessage = this.aiContext + message;
        
        // 创建EventSource连接
        eventSource = new EventSource(
          `http://localhost:2626/api/chat/send?chatId=${this.currentChatId}&uid=${uid}&message=${encodeURIComponent(fullMessage)}`
        );

        eventSource.onmessage = (event) => {
          if (event.data === '[DONE]') {
            // 流式回复结束
            eventSource.close();
            this.chatHistory[aiMessageIndex].loading = false;
            this.chatHistory[aiMessageIndex].text = fullResponse;
            this.chatHistory[aiMessageIndex].displayedText = fullResponse;
            this.$nextTick(() => this.scrollToBottom());
            
            // 保存到数据库
            this.saveMessageToDatabase(message, fullResponse, uid);
          } else {
            // 接收流式数据
            const chunk = event.data;
            fullResponse += chunk;
            
            // 更新显示文本（打字机效果）
            this.updateStreamingText(aiMessageIndex, fullResponse);
          }
        };

        eventSource.onerror = (error) => {
          console.error('EventSource错误:', error);
          if (eventSource) {
            eventSource.close();
          }
          
          if (fullResponse === '') {
            this.chatHistory[aiMessageIndex].loading = false;
            this.chatHistory[aiMessageIndex].text = '抱歉，AI回复出现错误，请稍后重试。';
            this.chatHistory[aiMessageIndex].displayedText = '抱歉，AI回复出现错误，请稍后重试。';
          } else {
            // 如果有部分回复，保存到数据库
            this.chatHistory[aiMessageIndex].loading = false;
            this.chatHistory[aiMessageIndex].text = fullResponse;
            this.chatHistory[aiMessageIndex].displayedText = fullResponse;
            this.saveMessageToDatabase(message, fullResponse, uid);
          }
        };

        // 设置超时处理
        setTimeout(() => {
          if (eventSource && eventSource.readyState !== EventSource.CLOSED) {
            console.warn('AI回复超时，关闭连接');
            eventSource.close();
            
            if (fullResponse === '') {
              this.chatHistory[aiMessageIndex].loading = false;
              this.chatHistory[aiMessageIndex].text = 'AI回复超时，请稍后重试。';
              this.chatHistory[aiMessageIndex].displayedText = 'AI回复超时，请稍后重试。';
            } else {
              // 保存部分回复
              this.chatHistory[aiMessageIndex].loading = false;
              this.chatHistory[aiMessageIndex].text = fullResponse;
              this.chatHistory[aiMessageIndex].displayedText = fullResponse;
              this.saveMessageToDatabase(message, fullResponse, uid);
            }
          }
        }, 60000); // 60秒超时

      } catch (error) {
        console.error('发送消息失败:', error);
        if (eventSource) {
          eventSource.close();
        }
        this.chatHistory[aiMessageIndex].loading = false;
        this.chatHistory[aiMessageIndex].text = '发送消息失败: ' + error.message;
        this.chatHistory[aiMessageIndex].displayedText = '发送消息失败: ' + error.message;
      }
    },

    // 保存消息到数据库
    async saveMessageToDatabase(userMessage, aiResponse, uid) {
      try {
        // 保存用户消息
        await this.$request.post('/api/chat/message', {
          chatId: this.currentChatId,
          uid: uid,
          message: userMessage,
          sender: 'user'
        });

        // 保存AI回复
        await this.$request.post('/api/chat/message', {
          chatId: this.currentChatId,
          uid: uid,
          message: aiResponse,
          sender: 'ai'
        });

        console.log('消息已保存到数据库');
      } catch (error) {
        console.error('保存消息到数据库失败:', error);
      }
    },

    // 更新流式文本显示
    updateStreamingText(messageIndex, fullText) {
      const item = this.chatHistory[messageIndex];
      
      // 使用Vue.set确保响应式更新
      this.$set(this.chatHistory, messageIndex, {
        ...item,
        loading: false,
        text: fullText,
        displayedText: fullText,
        updateTime: Date.now() // 添加时间戳强制重新渲染
      });
      
      // 延迟滚动，确保DOM更新完成
      this.$nextTick(() => {
        this.scrollToBottom();
      });
    },

    // 生成思考过程的HTML
    generateThinkingHTML(thinkingContent) {
      return `<div class="ai-thinking" style="margin: 5px 0 20px 0; border: 2px solid #e3f2fd; border-radius: 16px; background: linear-gradient(135deg, #f0f8ff 0%, #e1f5fe 100%); overflow: hidden; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); position: relative; opacity: 0.9;">
        <div class="thinking-header" style="background: linear-gradient(135deg, #2196f3 0%, #1976d2 100%); padding: 12px 20px 12px 10px; font-size: 14px; color: white; border-bottom: 1px solid #1976d2; display: flex; align-items: center; font-weight: 600; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);">
          <i class="el-icon-ice-cream-round" style="margin-right:5px; color: #ffd700; font-size: 18px; animation: thinking-pulse 2s infinite;"></i>
          DeepSeek思考过程
        </div>
        <div class="thinking-content" style="padding: 5px 10px 10px 10px; font-size: 14px; line-height: 1.7; color: #4a90e2 !important; background: #f8f9fa; font-style: italic; position: relative; ">
          ${thinkingContent}
        </div>
      </div>`;
    },

    // 格式化AI回复内容
    formatAIResponse(text) {
      if (!text) return '';
      
      let formattedText = text;
      
      // 暂时禁用预处理，避免破坏LaTeX公式
      // formattedText = this.preprocessLatexFormulas(formattedText);
      
      // 简单的预处理：只修复最基本的格式问题
      formattedText = this.preprocessLatexFormulas(formattedText);
      
      // 1. 先处理思考过程 <think>...</think> - 在Markdown渲染之前处理
      let thinkingContent = '';
      let thinkingProcessed = false;
      
      formattedText = formattedText.replace(
        /<think>([\s\S]*?)<\/think>([\s\S]*)/g,
        (match, thinking, content) => {
          thinkingProcessed = true;
          // 保存思考内容，稍后处理
          thinkingContent = thinking;
          console.log(thinkingContent)
          // 返回一个占位符，避免被Markdown处理
          return `<!--THINKING_PLACEHOLDER-->${content}`;
        }
      );
      
      // 2. 处理数学公式 - 改进LaTeX处理
      if (katex) {
        // 处理块级数学公式 $$...$$
        formattedText = formattedText.replace(/\$\$([\s\S]*?)\$\$/g, (match, formula) => {
          try {
            // 处理LaTeX命令
            const processedFormula = this.processLatexCommands(formula.trim());
            return katex.renderToString(processedFormula, { 
              displayMode: true,
              throwOnError: false,
              errorColor: '#cc0000'
            });
          } catch (e) {
            console.error('块级公式渲染失败:', e);
            return `<div class="math-error">公式渲染失败: ${formula}</div>`;
          }
        });

        // 处理行内数学公式 $...$
        formattedText = formattedText.replace(/\$([^$\n]+?)\$/g, (match, formula) => {
          try {
            // 处理LaTeX命令
            const processedFormula = this.processLatexCommands(formula.trim());
            return katex.renderToString(processedFormula, { 
              displayMode: false,
              throwOnError: false,
              errorColor: '#cc0000'
            });
          } catch (e) {
            console.error('行内公式渲染失败:', e);
            return `<span class="math-error">公式渲染失败: ${formula}</span>`;
          }
        });
      }
      
      // 3. 处理Markdown格式
      if (marked) {
        try {
          // 配置marked选项
          marked.setOptions({
            highlight: function(code, lang) {
              if (lang && hljs && hljs.getLanguage(lang)) {
                try {
                  return hljs.highlight(code, { language: lang }).value;
                } catch (err) {}
              }
              return code;
            },
            breaks: true,
            gfm: true
          });

          // 渲染Markdown
          formattedText = marked(formattedText);
        } catch (error) {
          console.error('Markdown渲染错误:', error);
          // 如果Markdown渲染失败，使用基础处理
          formattedText = this.basicAIFormatting(text);
          return formattedText;
        }
      } else {
        // 如果没有marked库，使用基础处理
        formattedText = this.basicAIFormatting(text);
        return formattedText;
      }
      
      // 4. 最后替换思考过程占位符
      if (thinkingProcessed) {
        formattedText = formattedText.replace(
          /<!--THINKING_PLACEHOLDER-->/g,
          () => {
            return this.generateThinkingHTML(thinkingContent);
          }
        );
      }
      
      return formattedText;
    },

    // 预处理LaTeX公式格式（暂时禁用）
    preprocessLatexFormulas(text) {
      // 暂时禁用预处理，避免破坏LaTeX公式
      let processed = text;
      
      // 只处理最基本的格式问题，避免破坏LaTeX公式
      
      // 1. 修复被换行分割的LaTeX命令（只处理明显的分割）
      processed = processed.replace(/(\\[a-zA-Z]+)\s*\n\s*/g, '$1');
      
      // 2. 修复常见的格式错误（只处理明显的错误）
      processed = processed.replace(/\\times([a-zA-Z])/g, '\\times $1');
      processed = processed.replace(/\\oplus([a-zA-Z])/g, '\\oplus $1');
      processed = processed.replace(/\\otimes([a-zA-Z])/g, '\\otimes $1');
      
      // 3. 移除重复的LaTeX公式
      processed = processed.replace(/(\$[^$]+\$)\s*\n\s*\1/g, '$1');
      
      return processed;
    },

    // 处理LaTeX命令
    processLatexCommands(formula) {
      let processed = formula;
      
      // 处理常见的LaTeX命令 - 确保它们被正确识别
      const latexCommands = [
        '\\bmod', '\\pmod', '\\mod', '\\text', '\\displaystyle', '\\sum', '\\prod',
        '\\left', '\\right', '\\begin', '\\end', '\\equiv', '\\leq', '\\geq', '\\sqrt',
        '\\frac', '\\cdots', '\\dots', '\\cdot', '\\times', '\\div', '\\pm', '\\mp',
        '\\oplus', '\\otimes', '\\odot', '\\ominus', '\\oslash', '\\bigoplus', '\\bigotimes',
        '\\alpha', '\\beta', '\\gamma', '\\delta', '\\epsilon', '\\zeta', '\\eta',
        '\\theta', '\\iota', '\\kappa', '\\lambda', '\\mu', '\\nu', '\\xi', '\\pi',
        '\\rho', '\\sigma', '\\tau', '\\upsilon', '\\phi', '\\chi', '\\psi', '\\omega',
        '\\Gamma', '\\Delta', '\\Theta', '\\Lambda', '\\Xi', '\\Pi', '\\Sigma', '\\Phi',
        '\\Psi', '\\Omega', '\\infty', '\\partial', '\\nabla', '\\forall', '\\exists',
        '\\nexists', '\\in', '\\notin', '\\subset', '\\supset', '\\cup', '\\cap',
        '\\emptyset', '\\varnothing', '\\mathbb', '\\mathbf', '\\mathit', '\\mathcal',
        '\\mathfrak', '\\mathscr', '\\mathsf', '\\mathtt', '\\mathrm', '\\mathnormal',
        '\\underbrace', '\\overbrace', '\\widehat', '\\widetilde', '\\overline',
        '\\underline', '\\vec', '\\hat', '\\tilde', '\\bar', '\\dot', '\\ddot',
        '\\prime', '\\backprime', '\\backslash', '\\setminus', '\\smallsetminus'
      ];
      
      // 处理所有LaTeX命令，确保它们被正确识别
      latexCommands.forEach(cmd => {
        const regex = new RegExp(cmd.replace(/\\/g, '\\\\'), 'g');
        processed = processed.replace(regex, cmd);
      });
      
      // 处理带参数的LaTeX命令
      // \text{...}
      processed = processed.replace(/\\text\{([^}]+)\}/g, '\\text{$1}');
      
      // \sqrt{...}
      processed = processed.replace(/\\sqrt\{([^}]+)\}/g, '\\sqrt{$1}');
      
      // \sqrt[n]{...}
      processed = processed.replace(/\\sqrt\[([^\]]+)\]\{([^}]+)\}/g, '\\sqrt[$1]{$2}');
      
      // \frac{...}{...}
      processed = processed.replace(/\\frac\{([^}]+)\}\{([^}]+)\}/g, '\\frac{$1}{$2}');
      
      // \underbrace{...}_{...}
      processed = processed.replace(/\\underbrace\{([^}]+)\}\{([^}]+)\}/g, '\\underbrace{$1}_{$2}');
      
      // \overbrace{...}^{...}
      processed = processed.replace(/\\overbrace\{([^}]+)\}\{([^}]+)\}/g, '\\overbrace{$1}^{$2}');
      
      // \begin{...} ... \end{...}
      processed = processed.replace(/\\begin\{([^}]+)\}([\s\S]*?)\\end\{([^}]+)\}/g, '\\begin{$1}$2\\end{$3}');
      
      // 处理数学字体命令
      processed = processed.replace(/\\mathbb\{([^}]+)\}/g, '\\mathbb{$1}');
      processed = processed.replace(/\\mathbf\{([^}]+)\}/g, '\\mathbf{$1}');
      processed = processed.replace(/\\mathit\{([^}]+)\}/g, '\\mathit{$1}');
      processed = processed.replace(/\\mathcal\{([^}]+)\}/g, '\\mathcal{$1}');
      processed = processed.replace(/\\mathfrak\{([^}]+)\}/g, '\\mathfrak{$1}');
      processed = processed.replace(/\\mathscr\{([^}]+)\}/g, '\\mathscr{$1}');
      processed = processed.replace(/\\mathsf\{([^}]+)\}/g, '\\mathsf{$1}');
      processed = processed.replace(/\\mathtt\{([^}]+)\}/g, '\\mathtt{$1}');
      processed = processed.replace(/\\mathrm\{([^}]+)\}/g, '\\mathrm{$1}');
      processed = processed.replace(/\\mathnormal\{([^}]+)\}/g, '\\mathnormal{$1}');
      
      // 处理修饰符命令
      processed = processed.replace(/\\widehat\{([^}]+)\}/g, '\\widehat{$1}');
      processed = processed.replace(/\\widetilde\{([^}]+)\}/g, '\\widetilde{$1}');
      processed = processed.replace(/\\overline\{([^}]+)\}/g, '\\overline{$1}');
      processed = processed.replace(/\\underline\{([^}]+)\}/g, '\\underline{$1}');
      processed = processed.replace(/\\vec\{([^}]+)\}/g, '\\vec{$1}');
      processed = processed.replace(/\\hat\{([^}]+)\}/g, '\\hat{$1}');
      processed = processed.replace(/\\tilde\{([^}]+)\}/g, '\\tilde{$1}');
      processed = processed.replace(/\\bar\{([^}]+)\}/g, '\\bar{$1}');
      processed = processed.replace(/\\dot\{([^}]+)\}/g, '\\dot{$1}');
      processed = processed.replace(/\\ddot\{([^}]+)\}/g, '\\ddot{$1}');
      
      // 处理特殊情况的括号匹配
      processed = processed.replace(/\(\\text\{([^}]+)\} \leq ([^)]+)\)/g, '(\\text{$1} \\leq $2)');
      processed = processed.replace(/\(\\text\{([^}]+)\} \geq ([^)]+)\)/g, '(\\text{$1} \\geq $2)');
      
      return processed;
    },

    // 基础AI内容格式化（fallback）
    basicAIFormatting(text) {
      if (!text) return '';
      
      let formattedText = text;
      
      // 处理思考过程 - 改进处理方式
      formattedText = formattedText.replace(
        /<think>([\s\S]*?)<\/think>([\s\S]*)/g,
        (match, thinking, content) => {
          const thinkingDiv = this.generateThinkingHTML(thinking);
          const contentDiv = content.trim() ? `<div class="ai-content">${content.trim()}</div>` : '';
          return thinkingDiv + contentDiv;
        }
      );
      
      // 处理数学符号和特殊字符
      formattedText = this.processMathSymbols(formattedText);
      
      // 处理标题
      formattedText = formattedText.replace(/^### (.*$)/gim, '<h3>$1</h3>');
      formattedText = formattedText.replace(/^## (.*$)/gim, '<h2>$1</h2>');
      formattedText = formattedText.replace(/^# (.*$)/gim, '<h1>$1</h1>');
      
      // 处理粗体和斜体
      formattedText = formattedText.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
      formattedText = formattedText.replace(/\*(.*?)\*/g, '<em>$1</em>');
      
      // 处理代码块
      formattedText = formattedText.replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>');
      formattedText = formattedText.replace(/`([^`]+)`/g, '<code>$1</code>');
      
      // 处理链接
      formattedText = formattedText.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
      
      // 处理列表 - 改进列表处理
      formattedText = this.processLists(formattedText);
      
      // 处理段落
      formattedText = formattedText.replace(/\n\n/g, '</p><p>');
      formattedText = formattedText.replace(/^(.+)$/gm, '<p>$1</p>');
      
      // 清理多余的p标签
      formattedText = formattedText.replace(/<p><\/p>/g, '');
      
      return formattedText;
    },

    // 处理数学符号和特殊字符
    processMathSymbols(text) {
      // 处理希腊字母和其他数学符号
      const mathSymbols = {
        'ω': '<span class="math-symbol">ω</span>',
        'η': '<span class="math-symbol">η</span>',
        '∇': '<span class="math-symbol">∇</span>',
        '←': '<span class="math-symbol">←</span>',
        '→': '<span class="math-symbol">→</span>',
        '∂': '<span class="math-symbol">∂</span>',
        '∑': '<span class="math-symbol">∑</span>',
        '∏': '<span class="math-symbol">∏</span>',
        '∫': '<span class="math-symbol">∫</span>',
        '∞': '<span class="math-symbol">∞</span>',
        '≠': '<span class="math-symbol">≠</span>',
        '≤': '<span class="math-symbol">≤</span>',
        '≥': '<span class="math-symbol">≥</span>',
        '∈': '<span class="math-symbol">∈</span>',
        '∉': '<span class="math-symbol">∉</span>',
        '⊂': '<span class="math-symbol">⊂</span>',
        '⊃': '<span class="math-symbol">⊃</span>',
        '∪': '<span class="math-symbol">∪</span>',
        '∩': '<span class="math-symbol">∩</span>',
        '∀': '<span class="math-symbol">∀</span>',
        '∃': '<span class="math-symbol">∃</span>',
        '∴': '<span class="math-symbol">∴</span>',
        '∵': '<span class="math-symbol">∵</span>'
      };
      
      let processedText = text;
      
      // 替换数学符号
      Object.keys(mathSymbols).forEach(symbol => {
        const regex = new RegExp(symbol, 'g');
        processedText = processedText.replace(regex, mathSymbols[symbol]);
      });
      
      // 处理数学表达式，如 L(D,ω) 中的括号
      processedText = processedText.replace(/\(([^)]*)\)/g, (match, content) => {
        // 如果括号内包含数学符号，保持原样
        if (/[ωη∇∂∑∏∫∞≠≤≥∈∉⊂⊃∪∩∀∃∴∵]/.test(content)) {
          return match;
        }
        return match;
      });
      
      return processedText;
    },

    // 处理列表
    processLists(text) {
      let processedText = text;
      
      // 先处理段落，避免列表处理影响段落
      const lines = processedText.split('\n');
      const processedLines = [];
      
      for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        
        // 处理无序列表项
        if (/^[\s]*[\-\*] (.*)$/.test(line)) {
          const content = line.replace(/^[\s]*[\-\*] /, '');
          processedLines.push(`<li>${content}</li>`);
        }
        // 处理有序列表项
        else if (/^[\s]*\d+\. (.*)$/.test(line)) {
          const content = line.replace(/^[\s]*\d+\. /, '');
          processedLines.push(`<li>${content}</li>`);
        }
        // 处理嵌套列表项（缩进的列表）
        else if (/^[\s]{2,}[\-\*] (.*)$/.test(line)) {
          const content = line.replace(/^[\s]*[\-\*] /, '');
          processedLines.push(`<li class="nested">${content}</li>`);
        }
        else if (/^[\s]{2,}\d+\. (.*)$/.test(line)) {
          const content = line.replace(/^[\s]*\d+\. /, '');
          processedLines.push(`<li class="nested">${content}</li>`);
        }
        // 非列表行保持原样
        else {
          processedLines.push(line);
        }
      }
      
      // 重新组合文本
      processedText = processedLines.join('\n');
      
      // 将连续的li标签包装在ul中
      processedText = processedText.replace(/(<li[^>]*>.*?<\/li>)/gs, (match) => {
        return `<ul>${match}</ul>`;
      });
      
      // 清理多余的ul标签
      processedText = processedText.replace(/<\/ul>\s*<ul>/g, '');
      
      return processedText;
    },

    // 滚动到最底部，让最新消息可见
    scrollToBottom() {
      const box = this.$refs.chatBox;
      if (box) box.scrollTop = box.scrollHeight;
    },
    // 保存当前聊天到 localStorage 并显示提示
    saveCurrentChat() {
      localStorage.setItem(
        "currentChatHistory",
        JSON.stringify(this.chatHistory)
      );
      // 同时保存当前会话ID
      if (this.currentChatId) {
        localStorage.setItem("currentChatId", this.currentChatId.toString());
      }
      this.showSavingTip();
    },
    showSavingTip() {
      this.isSaving = true;
      clearTimeout(this.savingTimer);
      this.savingTimer = setTimeout(() => {
        this.isSaving = false;
      }, 1000);
    },
    async fetchFiles() {
      const uid = getCurrentUserId() || 1  // 或你从 Vuex 拿 uid
      
      try {
        console.log("=== 开始加载文件列表 ===");
        console.log("用户ID:", uid);
        
        // 只获取UserPdf文件列表
        const userPdfRes = await this.$request.get('/pdf/selectByPage', {
          params: {
            uid: uid,
            pageNum: 1,
            pageSize: 100
          }
        });

        console.log("后端响应:", userPdfRes);

        if (userPdfRes.code == '200') {
          console.log("后端返回的原始数据:", userPdfRes.data.records); // 调试信息
          
          const userPdfFiles = userPdfRes.data.records.map(item => {
            console.log("处理单个文件:", item); // 调试每个文件
            console.log("文件ID:", item.id, "类型:", typeof item.id);
            console.log("previewUrl字段:", item.previewUrl); // 调试previewUrl字段
            
            return {
              id: item.id.toString(), // 确保ID是字符串类型
              name: item.name,
              type: 'userPdf',  // 标识为UserPdf
              status: 'success',
              createdAt: item.uploadTime,
              preview_url: item.previewUrl,
              parentFolderName: item.parentFolderName
            };
          });
          
          console.log("处理后的文件列表:", userPdfFiles); // 调试信息
          this.files = userPdfFiles;
          
          // 文件加载完成后，检查是否有路由参数需要处理
          const fileId = this.$route.query.fileId;
          if (fileId) {
            console.log("文件加载完成，处理路由参数:", fileId);
            // 使用nextTick确保DOM更新完成后再处理
            this.$nextTick(() => {
              this.handleFileIdChange(fileId);
            });
          } else {
            console.log("没有路由参数需要处理");
          }
        } else {
          this.$message.error('文件加载失败：' + userPdfRes.msg);
        }
      } catch (e) {
        console.error("文件加载失败:", e);
        this.$message.error('文件加载请求失败，请检查网络或后端接口');
      }
    },
    async loadPdfAndMarkdown(file) {
      console.log("loadPdfAndMarkdown被调用，文件对象:", file); // 调试信息
      
      if (!file || !file.name) {
        console.warn("文件信息不完整");
        return;
      }

      try {
        // 设置PDF URL
        this.currentPdfUrl = file.preview_url;
        console.log("设置PDF URL:", this.currentPdfUrl); // 调试信息
        console.log("文件的preview_url字段:", file.preview_url); // 调试信息
        
        // 获取Markdown文件内容
        await this.loadMarkdownFile(file);
        
        this.$message.success("文件加载成功");

        console.log("PDF URL:",this.currentPdfUrl);
        console.log("Markdown URL:",this.currentMarkdownContent);
      } catch (err) {
        console.error("❌ 文件加载失败", err);
        this.$message.error("文件加载失败：" + err.message);
      }
    },

    async loadMarkdownFile(file) {
      if (!file || !file.id) {
        throw new Error("文件信息不完整");
      }

      this.markdownLoading = true;
      
      try {
        // 调用后端接口获取Markdown内容
        console.log("调用后端接口获取Markdown内容，文件ID:", file.id);
        const response = await this.$request.get('/file/getMarkdownContent', {
          params: {
            fileId: file.id
          }
        });

        console.log("Markdown接口响应:", response);

        if (response.code === '200') {
          this.currentMarkdownContent = response.data || "Markdown文件内容为空";
          console.log("获取到的Markdown内容:", this.currentMarkdownContent);
        } else {
          this.currentMarkdownContent = "无法加载Markdown文件";
          console.warn("Markdown文件加载失败:", response.msg);
        }
      } catch (error) {
        console.error("加载Markdown文件失败:", error);
        this.currentMarkdownContent = "加载Markdown文件时发生错误";
      } finally {
        this.markdownLoading = false;
      }
    },

    testMarkdownRender() {
      console.log("=== Markdown渲染测试 ===");
      console.log("当前文件:", this.currentFile);
      console.log("当前Markdown内容:", this.currentMarkdownContent);
      
      // 测试数学公式
      const testFormula = "$$\\begin{array}{l}{P r[G_{\\mu}^{'}(M)=t]=P r[G_{\\mu}(M)+\\chi=t]}\\\\{=P r[\\chi=t-G_{\\mu}(M)]}\\\\{=\\displaystyle\\frac{\\epsilon}{2\\Delta G}\\exp(\\frac{-\\epsilon|t-G_{\\mu}(M)|}{\\Delta G})}\\end{array}$$";
      console.log("测试公式:", testFormula);
      
      if (katex) {
        try {
          const rendered = katex.renderToString(testFormula.replace(/\$\$/g, ''), {
            displayMode: true,
            throwOnError: false,
            errorColor: '#cc0000'
          });
          console.log("KaTeX渲染结果:", rendered);
        } catch (e) {
          console.error("KaTeX渲染失败:", e);
        }
      } else {
        console.warn("KaTeX未安装");
      }
      
      // 测试图片URL构建
      const testImageMarkdown = "![](images/test.jpg)";
      console.log("测试图片Markdown:", testImageMarkdown);
      
      const fileId = this.currentFile?.id || '';
      console.log("文件ID:", fileId);
      
      // 测试新的图片URL构建方式
      const imageUrl = `/file/previewImage?fileId=${fileId}&imageName=test.jpg`;
      
      console.log("图片URL构建测试:");
      console.log("文件ID:", fileId);
      console.log("生成的图片URL:", imageUrl);
      
      this.$message.info("测试完成，请查看控制台输出");
    },

    // 测试AI回复格式化功能
    testAIResponseFormatting() {
      const testCases = [
        {
          name: '基础思考过程',
          text: '<think>这是一个思考过程，分析问题...</think>这是AI的回复内容。'
        },
        {
          name: '数学公式测试',
          text: '考虑函数 $f(x) = x^2 + 2x + 1$，当 $x \\bmod N = 0$ 时，我们有 $f(x) \\equiv 1 \\pmod{N}$。'
        },
        {
          name: '复杂LaTeX命令',
          text: '对于表达式 $\\text{Cov}(X,Y) \\leq \\sqrt{\\text{Var}(X) \\cdot \\text{Var}(Y)}$，我们有 $\\underbrace{\\sum_{i=1}^{n} x_i}_{\\text{总和}} \\equiv 0 \\bmod{p}$。'
        },
        {
          name: '希腊字母和符号',
          text: '设 $\\alpha, \\beta \\in \\mathbb{R}$，则 $\\alpha + \\beta \\leq \\infty$ 且 $\\partial f/\\partial x \\neq 0$。'
        },
        {
          name: '块级公式',
          text: '<think>分析这个数学问题...</think>根据定义，我们有：\n\n$$\\frac{\\partial L}{\\partial \\omega} = \\sum_{i=1}^{n} \\left(\\frac{\\partial L}{\\partial f_i} \\cdot \\frac{\\partial f_i}{\\partial \\omega}\\right)$$\n\n其中 $L$ 是损失函数，$\\omega$ 是参数。'
        },
        {
          name: '流式输出测试',
          text: '在流式输出过程中，LaTeX公式应该正确渲染：$\\bmod N$、$\\pmod{p}$、$\\text{函数名}$ 等。'
        },
        {
          name: '加密算法测试',
          text: '同态加密的性质：$E(a)\\oplus E(b)=E(a+b)$ 和 $E(a)\\otimes E(b)=E(a\\times b)$。'
        },
        {
          name: '格式错误修复测试',
          text: '测试格式错误的LaTeX：E(a)\\oplusE(b)=E(a+b)和\nE\n(\na\n)\n\\otimesE\n(\nb\n)\n=\nE\n(\na\n\\timesb\n)\nE(a)\\otimesE(b)=E(a\\timesb)'
        }
      ];

      console.log('=== AI回复格式化测试 ===');
      testCases.forEach((testCase, index) => {
        console.log(`\n测试 ${index + 1}: ${testCase.name}`);
        console.log('原始文本:', testCase.text);
        const formatted = this.formatAIResponse(testCase.text);
        console.log('格式化结果:', formatted);
        
        // 检查是否包含LaTeX错误
        if (formatted.includes('公式渲染失败')) {
          console.warn('⚠️ LaTeX渲染失败');
        } else {
          console.log('✅ 格式化成功');
        }
      });
      
      this.$message.success('测试完成，请查看控制台输出');
    },

    onPdfLoaded(pdfInfo) {
      console.log("PDF加载完成", pdfInfo);
      this.pdfTotalPages = pdfInfo.totalPages;
      this.currentPdfPage = Math.min(this.currentPdfPage, pdfInfo.totalPages);
    },
    onPdfError(error) {
      console.error("PDF加载失败", error);
      this.$message.error("PDF加载失败: " + error.message);
    },
    onPdfPageRendered(pageInfo) {
      console.log("PDF页面渲染完成", pageInfo);
      // 可以在这里添加页面渲染完成后的逻辑
    },
    onPdfPageChanged(newPage) {
      console.log("PDF页面变化", newPage);
      this.currentPdfPage = newPage;
    },

    prevPdfPage() {
      if (this.currentPdfPage > 1) {
        this.currentPdfPage--;
      }
    },
    nextPdfPage() {
      if (this.currentPdfPage < this.pdfTotalPages) {
        this.currentPdfPage++;
      }
    },

    // 切换栏位选中状态
    togglePane(pane) {
      const index = this.selectedPanes.indexOf(pane);
      
      if (index > -1) {
        // 如果已经选中，则取消选中（但至少保留一个栏位）
        if (this.selectedPanes.length > 1) {
          this.selectedPanes.splice(index, 1);
        }
      } else {
        // 如果未选中，则添加到选中列表（最多两个栏位）
        if (this.selectedPanes.length < 2) {
          this.selectedPanes.push(pane);
        } else {
          // 如果已经有2个栏位，则替换最后一个
          this.selectedPanes.pop();
          this.selectedPanes.push(pane);
        }
      }
    },

    // 检查会话状态
    async checkSessionStatus() {
      if (!this.currentChatId) return;
      
      try {
        const uid = parseInt(getCurrentUserId() || "1");
        const sessionsResponse = await this.$request.get(`/api/chat/sessions/${uid}`);
        
        if (sessionsResponse.code === '200') {
          const currentSession = sessionsResponse.data.find(session => session.chatId === this.currentChatId);
          
          if (!currentSession || currentSession.state !== 'active') {
            // 如果会话不存在或不是活跃状态，清空当前会话
            console.log('当前会话已失效，清空对话历史');
            this.chatHistory = [];
            this.currentChatId = null;
            localStorage.removeItem("currentChatHistory");
            localStorage.removeItem("currentChatId");
          }
        }
      } catch (error) {
        console.error('检查会话状态失败:', error);
      }
    },

    // 加载活跃的对话会话
    async loadActiveChatSession() {
      try {
        // 获取当前文件ID
        const currentFileId = this.currentFileId;
        if (!currentFileId) {
          console.log('没有选中文件，不加载对话');
          return;
        }

        const uid = parseInt(getCurrentUserId() || "1");
        
        // 尝试获取当前PDF的最新活跃会话
        const sessionResponse = await this.$request.get(`/api/chat/session/latest/${uid}/${currentFileId}`);
        
        if (sessionResponse.code === '200' && sessionResponse.data) {
          // 找到活跃会话，加载其消息
          const session = sessionResponse.data;
          await this.loadChatHistory(session.chatId);
          console.log('已加载PDF对应的活跃对话会话:', session.chatId);
        } else {
          // 没有找到活跃会话，清空对话历史
          console.log('没有找到PDF对应的活跃会话，清空对话历史');
          this.chatHistory = [];
          this.currentChatId = null;
          localStorage.removeItem("currentChatHistory");
          localStorage.removeItem("currentChatId");
        }
      } catch (error) {
        console.error('加载活跃对话会话失败:', error);
        // 如果加载失败，清空对话历史
        this.chatHistory = [];
        this.currentChatId = null;
        localStorage.removeItem("currentChatHistory");
        localStorage.removeItem("currentChatId");
      }
    },

    // 从localStorage恢复对话历史
    loadFromLocalStorage() {
      const savedChat = localStorage.getItem("currentChatHistory");
      const savedChatId = localStorage.getItem("currentChatId");
      const savedAIContext = localStorage.getItem("aiContext");
      
      if (savedChat) {
        this.chatHistory = JSON.parse(savedChat);
        this.currentChatId = savedChatId ? parseInt(savedChatId) : null;
        this.$nextTick(() => this.scrollToBottom());
        console.log('从localStorage恢复对话历史');
      }
      
      // 恢复AI上下文设置
      if (savedAIContext) {
        this.aiContext = savedAIContext;
        console.log('从localStorage恢复AI上下文设置');
      }
    },

    // 设置AI上下文
    setAIContext() {
      this.$prompt('设置AI对话上下文', '请输入AI的预设角色和任务描述：', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        inputType: 'textarea',
        inputValue: this.aiContext,
        inputPlaceholder: '例如：假设你是一位科研工作者...',
        inputValidator: (value) => {
          if (!value || value.trim().length === 0) {
            return '上下文不能为空';
          }
          return true;
        }
      }).then(({ value }) => {
        if (value) {
          this.aiContext = value.trim();
          // 保存到localStorage
          localStorage.setItem('aiContext', this.aiContext);
          this.$message.success('AI上下文设置成功');
        }
      }).catch(() => {
        // 用户取消
      });
    },

    // 重置AI上下文为默认值
    resetAIContext() {
      this.aiContext = "假设你是一位科研工作者，你正在研读一篇学术论文，我可能有一些专业知识需要你解释，或者我会让你翻译一段markdown文本，注意在翻译结果中保留其中的latex公式。请用中文回答。\n\n";
      localStorage.setItem('aiContext', this.aiContext);
      this.$message.success('AI上下文已重置为默认值');
    },

    // 获取AI上下文摘要
    getContextSummary() {
      if (!this.aiContext) return '';
      
      // 移除换行符和多余空格
      const cleanContext = this.aiContext.replace(/\n+/g, ' ').replace(/\s+/g, ' ').trim();
      
      // 如果长度超过50个字符，截取前50个字符并添加省略号
      if (cleanContext.length > 50) {
        return cleanContext.substring(0, 50) + '...';
      }
      
      return cleanContext;
    },

    // 显示右键菜单
    showContextMenu(event) {
      event.preventDefault();
      
      this.$confirm('是否要重置AI上下文为默认值？', '重置AI上下文', {
        confirmButtonText: '重置',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.resetAIContext();
      }).catch(() => {
        // 用户取消
      });
    },

    // 调试LaTeX处理
    debugLatexProcessing() {
      const testFormula = '\\underbrace{\\frac{n}{\\frac{1}{x_1} + \\frac{1}{x_2} + \\dots + \\frac{1}{x_n}}}_{\\text{调和平均 (HM)}} \\leq \\underbrace{\\sqrt[n]{x_1 x_2 \\cdots x_n}}_{\\text{几何平均 (GM)}}';
      
      console.log("=== LaTeX处理调试 ===");
      console.log("原始公式:", testFormula);
      
      const processed = this.processLatexCommands(testFormula);
      console.log("处理后公式:", processed);
      
      if (katex) {
        try {
          const rendered = katex.renderToString(processed, {
            displayMode: true,
            throwOnError: false,
            errorColor: '#cc0000'
          });
          console.log("KaTeX渲染结果:", rendered);
          
          // 添加到聊天历史进行测试
          this.chatHistory.push({
            text: `<think>调试LaTeX处理过程</think>

### **LaTeX公式测试**

测试公式：
$$${testFormula}$$

渲染结果：
${rendered}`,
            displayedText: `<think>调试LaTeX处理过程</think>

### **LaTeX公式测试**

测试公式：
$$${testFormula}$$

渲染结果：
${rendered}`,
            type: "ai",
            loading: false,
          });
          
          this.$nextTick(() => this.scrollToBottom());
          this.$message.info("LaTeX处理调试完成，请查看聊天区域和控制台");
        } catch (e) {
          console.error("KaTeX渲染失败:", e);
          this.$message.error("LaTeX渲染失败: " + e.message);
        }
      } else {
        this.$message.warning("KaTeX未安装，无法测试渲染");
      }
    },

    // 处理文件ID变化
    handleFileIdChange(fileId) {
      console.log("=== 处理文件ID变化 ===");
      console.log("传入的fileId:", fileId, "类型:", typeof fileId);
      console.log("当前文件列表长度:", this.files.length);
      console.log("当前文件列表:", this.files);
      
      if (!fileId) {
        console.warn("fileId为空，跳过处理");
        return;
      }
      
      // 尝试多种方式查找文件，处理ID类型转换问题
      let file = null;
      
      // 1. 直接匹配
      file = this.files.find(f => f.id === fileId);
      if (file) {
        console.log("直接匹配成功:", file);
      } else {
        // 2. 字符串转换匹配
        file = this.files.find(f => f.id.toString() === fileId.toString());
        if (file) {
          console.log("字符串转换匹配成功:", file);
        } else {
          // 3. 数字转换匹配
          file = this.files.find(f => parseInt(f.id) === parseInt(fileId));
          if (file) {
            console.log("数字转换匹配成功:", file);
          }
        }
      }
      
      if (!file) {
        console.error("未找到对应的文件:", fileId);
        console.error("可用的文件ID:", this.files.map(f => ({ id: f.id, type: typeof f.id, name: f.name })));
        this.$message.warning('未找到对应文件，请检查文件列表是否已加载');
        return;
      }

      console.log("找到文件:", file);
      this.selectedPanes = ['pdf'];
      
      // 加载PDF和Markdown文件
      this.loadPdfAndMarkdown(file);
      
      // 加载对应PDF的对话历史
      this.loadActiveChatSession();

      this.$nextTick(() => {
        // 重置各 Pane 的滚动条
        const leftInner = this.$el.querySelector(".left-pane .pane-inner");
        const chatBox = this.$refs.chatBox;
        if (leftInner) leftInner.scrollTop = 0;
        if (chatBox) chatBox.scrollTop = 0;
      });
    },
  },
  mounted() {
    this.fetchFiles();
    this.loadActiveChatSession();
  },
  watch: {
    '$route.query.fileId': {
      immediate: true,
      handler(newId) {
        if (!newId) return;
        
        // 确保文件列表已加载
        if (this.files.length === 0) {
          // 如果文件列表为空，等待文件加载完成后再处理
          this.$nextTick(() => {
            this.handleFileIdChange(newId);
          });
        } else {
          this.handleFileIdChange(newId);
        }
      }
    }
  },
};
</script>

<style scoped>
/* ===================================================
   1. 最外层禁止滚动：overflow:hidden
   =================================================== */
.notes-page {
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  margin: 0;
  padding: 0;
}

/* ===================================================
   2. 左侧 Aside 样式
   =================================================== */
.notes-aside {
  background: #fafafa;
  border-right: 1px solid #ececec;
  position: relative;
  transition: width 0.3s;
  overflow: hidden; /* 禁止 Aside 自身滚动 */
}
/* 收缩时隐藏筛选/搜索按钮与菜单文字 */
.notes-aside.collapsed .aside-tools,
.notes-aside.collapsed .notes-menu span,
.notes-aside.collapsed .status-badge {
  display: none;
}
/* "折叠/展开" 按钮区域 */
.aside-toggle {
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}
/* 筛选 & 搜索 按钮水平排列 */
.aside-tools {
  display: flex;
  justify-content: space-around;
  padding: 8px 0;
}
/* 文件列表，除去顶部 104px 空间后可滚 */
.notes-menu {
  border: none;
  height: calc(100% - 104px); /* 48px(toggle) + 56px(tools) = 104px */
  overflow-y: auto;
}
/* 图标对齐 */
.notes-menu .el-menu-item i {
  font-size: 20px;
  width: 24px;
  text-align: center;
}
/* PDF菜单项样式 */
.pdf-menu-item {
  display: flex;
  align-items: center;
  padding: 0 20px;
  position: relative;
  height: 48px;
  transition: all 0.3s ease;
}

.pdf-menu-item:hover {
  background-color: #f5f7fa;
}

.pdf-icon {
  width: 20px;
  height: 20px;
  margin-right: 8px;
  flex-shrink: 0;
  object-fit: contain;
}

/* 折叠状态下的PDF图标 */
.notes-aside.collapsed .pdf-icon {
  margin: 0;
  display: block;
  width: 24px;
  height: 24px;
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
}

/* 折叠状态下的菜单项布局 */
.notes-aside.collapsed .pdf-menu-item {
  justify-content: center;
  padding: 0;
  position: relative;
  width: 48px;
}

/* 折叠状态下隐藏状态徽章 */
.notes-aside.collapsed .status-badge {
  display: none;
}

.pdf-filename {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 14px;
  color: #303133;
  min-width: 0;
  padding-right: 40px; /* 为状态徽章留出空间 */
  line-height: 1.4;
}

/* 折叠状态下隐藏文件名 */
.notes-aside.collapsed .pdf-filename {
  display: none;
}

/* 折叠状态下隐藏状态徽章 */
.notes-aside.collapsed .status-badge {
  display: none;
}

/* 状态徽章在菜单项右上角 */
.status-badge {
  position: absolute;
  top: 50%;
  right: 8px;
  transform: translateY(-50%);
  z-index: 1;
  font-size: 10px;
  padding: 0 4px;
  height: 16px;
  line-height: 16px;
}

/* 筛选 & 搜索 弹窗内部 */
.filter-form {
  padding: 8px;
}
.tools-btns {
  display: flex;
  justify-content: flex-end;
  margin-top: 8px;
}
.search-pop {
  padding: 8px;
}
.search-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 8px;
}

/* ===================================================
   3. 右侧整体容器：禁止整体滚动
   =================================================== */
.outer-container {
  display: flex;
  flex: 1;
  overflow: hidden;
  margin: 0;
  padding: 0;
}

/* ===================================================
   4. 顶部操作栏（Header）
   =================================================== */
.notes-header {
  background: #fff;
  border-bottom: 1px solid #ececec;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0;
  height: 48px;
  flex-shrink: 0;
  margin: 0;
}
.header-left {
  font-size: 16px;
  font-weight: 600;
  width: 300px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex-shrink: 0;
  padding: 0 16px;
}
.header-center {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
}
.header-center .tab-item {
  display: flex;
  align-items: center;
  padding: 8px 16px;
  margin: 0 4px;
  border-radius: 20px;
  cursor: pointer;
  transition: all 0.3s ease;
  font-size: 14px;
  color: #606266;
  background: #f5f7fa;
  border: 1px solid transparent;
}
.header-center .tab-item:hover {
  background: #ecf5ff;
  color: #409eff;
}
.header-center .tab-item.active {
  color: #409eff;
  background: #ecf5ff;
  border-color: #409eff;
  font-weight: 500;
}
.header-center .tab-item i {
  margin-right: 6px;
  font-size: 16px;
}
.header-right {
  padding: 0 16px;
}
.header-right .el-button {
  margin-left: 8px;
}

/* ===================================================
   5. 右侧主区域：包含"Header + Main"
   =================================================== */
.right-container {
  display: flex;
  flex-direction: column;
  flex: 1;
  overflow: hidden; /* 禁止右侧整体滚动 */
  margin: 0;
  padding: 0;
}
.notes-main {
  flex: 1;
  display: flex;
  overflow: hidden; /* 禁止整体滚动 */
  padding: 0 !important;
}

/* ===================================================
   6. 各 Pane（原文 / 提取 / 对话）
   =================================================== */
/* Pane 本身禁止滚动，只能 Pane-inner/Chat-container 滚动 */
.pane {
  flex: 1;
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: #fff;
  margin: 0;
  padding: 0;
}
/* Pane-inner 是可滚动内容区 */
.pane-inner {
  flex: 1;
  overflow-y: auto;
  padding: 0;
}
.placeholder {
  color: #999;
  text-align: center;
  margin-top: 100px;
}
.left-pane {
  border-right: 1px solid #ececec;
}
.right-pane {
  border-left: 1px solid #ececec;
}
.single-pane {
  border: none !important;
}

/* PDF缩放控制栏 */
.pdf-controls {
  position: sticky;
  top: 0;
  background: #f5f7fa;
  padding: 8px 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid #e4e7ed;
  z-index: 10;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

/* PDF预览容器 */
.pdf-container {
  width: 100%;
  height: calc(100vh - 48px); /* 减去header的高度 */
  overflow: hidden;
  background: #f8f9fa;
  position: relative;
}

/* ===================================================
   7. 对话 Pane 详细布局
   =================================================== */
.chat-pane {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.chat-layout {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* 聊天消息区：独立滚动 */
.chat-container {
  flex: 1;
  overflow-y: auto;
  padding: 0;
  background: #fafafa;
}
.chat-item {
  display: flex;
  margin-bottom: 12px;
  padding: 0 16px;
}
.chat-item.user {
  justify-content: flex-end;
}
.chat-item.ai {
  justify-content: flex-start;
}
.chat-bubble {
  max-width: 85%;
  padding: 12px 16px;
  border-radius: 16px;
  word-break: break-word;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
  overflow-wrap: break-word;
  word-wrap: break-word;
}
.chat-item.user .chat-bubble {
  background: #00274e;
  color: #fff;
}
.chat-item.ai .chat-bubble {
  background: #fff;
  color: #303133;
  max-width: 90%;
}

/* 聊天输入区：固定在对话 Pane 底部 */
.chat-input-area-inside {
  flex: 0;
  display: flex;
  align-items: center;
  padding: 12px 16px;
  background: #f5f7fa;
  box-shadow: 0 -1px 4px rgba(0, 0, 0, 0.05);
}

/* 自动增高的多行输入框 */
.input-field-textarea {
  flex: 1;
  font-size: 14px;
  line-height: 1.5;
  resize: none; /* 禁止用户手动拉伸 */
}
.input-field-textarea .el-textarea__inner {
  font-size: 14px;
  line-height: 1.5;
  resize: none;
}

/* 发送按钮 */
.send-button {
  margin-left: 8px;
  flex-shrink: 0;
}

/* 新建对话按钮 */
.new-chat-btn {
  margin-right: 8px;
  flex-shrink: 0;
  color: #409eff;
}

.new-chat-btn:hover {
  color: #66b1ff;
}

/* 设置AI上下文按钮 */
.context-btn {
  margin-right: 8px;
  flex-shrink: 0;
  color: #67c23a;
}

.context-btn:hover {
  color: #85ce61;
}

/* 测试按钮 */
.test-btn {
  margin-right: 8px;
  flex-shrink: 0;
  color: #e6a23c;
}

.test-btn:hover {
  color: #ebb563;
}

/* Markdown内容样式 */
.loading-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 200px;
  color: #909399;
}

.loading-container i {
  font-size: 24px;
  margin-bottom: 8px;
}

.markdown-content {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-size: 14px;
  line-height: 1.6;
  color: #333;
  background: #fff;
  padding: 20px;
  max-height: 100%;
  overflow-y: auto;
}

/* Markdown标题样式 */
.markdown-content h1 {
  font-size: 2em;
  margin: 1.5em 0 0.5em 0;
  padding-bottom: 0.3em;
  border-bottom: 1px solid #eaecef;
  color: #24292e;
}

.markdown-content h2 {
  font-size: 1.5em;
  margin: 1.2em 0 0.4em 0;
  padding-bottom: 0.2em;
  border-bottom: 1px solid #eaecef;
  color: #24292e;
}

.markdown-content h3 {
  font-size: 1.25em;
  margin: 1em 0 0.3em 0;
  color: #24292e;
}

.markdown-content h4 {
  font-size: 1em;
  margin: 0.8em 0 0.2em 0;
  color: #24292e;
}

.markdown-content h5 {
  font-size: 0.875em;
  margin: 0.6em 0 0.2em 0;
  color: #24292e;
}

.markdown-content h6 {
  font-size: 0.85em;
  margin: 0.5em 0 0.2em 0;
  color: #24292e;
}

/* Markdown段落和列表 */
.markdown-content p {
  margin: 0.8em 0;
  line-height: 1.6;
}

.markdown-content ul, .markdown-content ol {
  margin: 0.8em 0;
  padding-left: 2em;
}

.markdown-content li {
  margin: 0.3em 0;
  line-height: 1.6;
}

/* Markdown代码块 */
.markdown-content pre {
  background: #f6f8fa;
  border: 1px solid #e1e4e8;
  border-radius: 3px;
  padding: 16px;
  overflow: auto;
  margin: 1em 0;
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
  font-size: 13px;
  line-height: 1.45;
}

.markdown-content code {
  background: #f6f8fa;
  border: 1px solid #e1e4e8;
  border-radius: 3px;
  padding: 0.2em 0.4em;
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
  font-size: 85%;
}

.markdown-content pre code {
  background: none;
  border: none;
  padding: 0;
  font-size: inherit;
}

/* Markdown引用 */
.markdown-content blockquote {
  margin: 1em 0;
  padding: 0 1em;
  color: #6a737d;
  border-left: 0.25em solid #dfe2e5;
  background: #f6f8fa;
}

/* Markdown表格 */
.markdown-content table {
  border-collapse: collapse;
  width: 100%;
  margin: 1em 0;
}

.markdown-content th,
.markdown-content td {
  border: 1px solid #dfe2e5;
  padding: 6px 13px;
  text-align: left;
}

.markdown-content th {
  background: #f6f8fa;
  font-weight: 600;
}

/* Markdown图片 */
.markdown-content img {
  max-width: 100%;
  height: auto;
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  margin: 1em 0;
}

/* Markdown链接 */
.markdown-content a {
  color: #0366d6;
  text-decoration: none;
}

.markdown-content a:hover {
  text-decoration: underline;
}

/* Markdown强调 */
.markdown-content strong {
  font-weight: 600;
  color: #24292e;
}

.markdown-content em {
  font-style: italic;
}

/* Markdown分割线 */
.markdown-content hr {
  height: 0.25em;
  padding: 0;
  margin: 24px 0;
  background-color: #e1e4e8;
  border: 0;
}

/* 数学公式样式 */
.markdown-content .katex {
  font-size: 1.1em;
}

.markdown-content .katex-display {
  margin: 1em 0;
  text-align: center;
}

/* 数学公式错误样式 */
.math-error {
  color: #cc0000;
  background: #ffe6e6;
  border: 1px solid #ffcccc;
  padding: 8px;
  border-radius: 4px;
  font-family: monospace;
  margin: 8px 0;
}

/* 代码高亮 */
.markdown-content .hljs {
  background: #f6f8fa;
  color: #24292e;
}

/* AI思考过程样式 */
.chat-bubble .ai-message .ai-thinking {
  margin: 20px 0 !important;
  border: 2px solid #e3f2fd !important;
  border-radius: 16px !important;
  background: linear-gradient(135deg, #f0f8ff 0%, #e1f5fe 100%) !important;
  overflow: hidden !important;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1) !important;
  position: relative !important;
  opacity: 0.9 !important;
}

.chat-bubble .ai-message .ai-thinking::before {
  content: '' !important;
  position: absolute !important;
  top: 0 !important;
  left: 0 !important;
  right: 0 !important;
  height: 4px !important;
  background: linear-gradient(90deg, #2196f3, #1976d2, #0d47a1) !important;
}

.chat-bubble .ai-message .thinking-header {
  background: linear-gradient(135deg, #2196f3 0%, #1976d2 100%) !important;
  padding: 12px 20px !important;
  font-size: 14px !important;
  color: white !important;
  border-bottom: 1px solid #1976d2 !important;
  display: flex !important;
  align-items: center !important;
  font-weight: 600 !important;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2) !important;
}

.chat-bubble .ai-message .thinking-header i {
  margin-right: 10px !important;
  color: #ffd700 !important;
  font-size: 18px !important;
  animation: thinking-pulse 2s infinite !important;
}

@keyframes thinking-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.8; transform: scale(1.1); }
}

.chat-bubble .ai-message .thinking-content {
  padding: 20px !important;
  font-size: 14px !important;
  line-height: 1.7 !important;
  color: #4a90e2 !important; /* 浅蓝色文字 */
  background: #f8f9fa !important;
  font-style: italic !important;
  position: relative !important;
  border-left: 4px solid #2196f3 !important;
}

.chat-bubble .ai-message .thinking-content::before {
  content: '"' !important;
  position: absolute !important;
  top: 10px !important;
  left: 10px !important;
  font-size: 24px !important;
  color: #2196f3 !important;
  font-family: serif !important;
  opacity: 0.5 !important;
}

/* 确保思考过程内的所有文字都是浅蓝色 - 使用更高优先级 */
.chat-bubble .ai-message .thinking-content,
.chat-bubble .ai-message .thinking-content *,
.chat-bubble .ai-message .thinking-content p,
.chat-bubble .ai-message .thinking-content h1,
.chat-bubble .ai-message .thinking-content h2,
.chat-bubble .ai-message .thinking-content h3,
.chat-bubble .ai-message .thinking-content h4,
.chat-bubble .ai-message .thinking-content h5,
.chat-bubble .ai-message .thinking-content h6,
.chat-bubble .ai-message .thinking-content strong,
.chat-bubble .ai-message .thinking-content em,
.chat-bubble .ai-message .thinking-content span,
.chat-bubble .ai-message .thinking-content div {
  color: #4a90e2 !important;
}

.chat-bubble .ai-message .thinking-content h1,
.chat-bubble .ai-message .thinking-content h2,
.chat-bubble .ai-message .thinking-content h3,
.chat-bubble .ai-message .thinking-content h4,
.chat-bubble .ai-message .thinking-content h5,
.chat-bubble .ai-message .thinking-content h6 {
  border-left-color: #4a90e2 !important;
}

/* AI内容区域样式 */
.chat-bubble .ai-message .ai-content {
  margin-top: 20px;
  padding: 20px;
  background: #fff;
  border-radius: 12px;
  border: 1px solid #e4e7ed;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  position: relative;
}

.chat-bubble .ai-message .ai-content::before {
  content: '📝 AI回答';
  position: absolute;
  top: -10px;
  left: 20px;
  background: #409eff;
  color: white;
  padding: 4px 12px;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 500;
}

/* AI消息中的Markdown样式 */
.chat-bubble .ai-message .ai-thinking + *,
.chat-bubble .ai-message > div:not(.ai-thinking) {
  margin-top: 16px;
}

.chat-bubble .ai-message .ai-thinking + *::before {
  content: '';
  display: block;
  height: 1px;
  background: linear-gradient(90deg, transparent, #e0e0e0, transparent);
  margin: 16px 0;
}

.chat-bubble .ai-message h1,
.chat-bubble .ai-message h2,
.chat-bubble .ai-message h3,
.chat-bubble .ai-message h4,
.chat-bubble .ai-message h5,
.chat-bubble .ai-message h6 {
  margin: 20px 0 12px 0;
  font-weight: 600;
  line-height: 1.25;
  color: #24292e;
  border-left: 4px solid #409eff;
  padding-left: 12px;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.chat-bubble .ai-message h1 {
  font-size: 1.6em;
  border-bottom: 2px solid #eaecef;
  padding-bottom: 0.4em;
}

.chat-bubble .ai-message h2 {
  font-size: 1.4em;
  border-bottom: 1px solid #eaecef;
  padding-bottom: 0.3em;
}

.chat-bubble .ai-message h3 {
  font-size: 1.2em;
}

.chat-bubble .ai-message h4 {
  font-size: 1.1em;
}

.chat-bubble .ai-message p {
  margin: 12px 0;
  line-height: 1.7;
  color: #333;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.chat-bubble .ai-message ul,
.chat-bubble .ai-message ol {
  margin: 12px 0;
  padding-left: 24px;
}

.chat-bubble .ai-message li {
  margin: 6px 0;
  line-height: 1.6;
  color: #333;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.chat-bubble .ai-message li.nested {
  margin-left: 20px;
  padding-left: 12px;
  border-left: 2px solid #e0e0e0;
  font-size: 0.95em;
  color: #666;
}

.chat-bubble .ai-message code {
  background: #f6f8fa;
  border: 1px solid #e1e4e8;
  border-radius: 4px;
  padding: 0.3em 0.5em;
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
  font-size: 85%;
  color: #d73a49;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.chat-bubble .ai-message pre {
  background: #f6f8fa;
  border: 1px solid #e1e4e8;
  border-radius: 6px;
  padding: 16px;
  overflow: auto;
  margin: 16px 0;
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
  font-size: 13px;
  line-height: 1.45;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  max-width: 100%;
}

.chat-bubble .ai-message pre code {
  background: none;
  border: none;
  padding: 0;
  font-size: inherit;
  color: inherit;
}

.chat-bubble .ai-message a {
  color: #0366d6;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-bottom-color 0.2s;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

.chat-bubble .ai-message a:hover {
  border-bottom-color: #0366d6;
}

.chat-bubble .ai-message strong {
  font-weight: 600;
  color: #24292e;
}

.chat-bubble .ai-message em {
  font-style: italic;
  color: #555;
}

/* 数学公式样式 */
.chat-bubble .ai-message .katex {
  font-size: 1.1em;
  max-width: 100%;
  overflow-x: auto;
  display: inline-block;
}

.chat-bubble .ai-message .katex-display {
  margin: 16px 0;
  text-align: center;
  padding: 16px;
  background: #f8f9fa;
  border-radius: 8px;
  border: 1px solid #e1e4e8;
  overflow-x: auto;
  max-width: 100%;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

.chat-bubble .ai-message .katex-display .katex {
  font-size: 1.2em;
  text-align: center;
  display: block;
  margin: 0 auto;
}

.chat-bubble .ai-message .katex .katex-html {
  overflow-x: auto;
  max-width: 100%;
}

.chat-bubble .ai-message .katex .katex-html .base {
  margin: 0.25em 0;
}

.chat-bubble .ai-message .katex .katex-html .strut {
  display: inline-block;
}

.chat-bubble .ai-message .katex .katex-html .underbrace {
  border-bottom: 0.04em solid;
  box-sizing: border-box;
  height: 0.2em;
  max-width: 1.25em;
  vertical-align: 0em;
  width: 100%;
}

.chat-bubble .ai-message .katex .katex-html .overbrace {
  border-top: 0.04em solid;
  box-sizing: border-box;
  height: 0.2em;
  max-width: 1.25em;
  vertical-align: 0em;
  width: 100%;
}

.chat-bubble .ai-message .math-error {
  color: #cc0000;
  background: #ffe6e6;
  border: 1px solid #ffcccc;
  padding: 12px;
  border-radius: 6px;
  font-family: monospace;
  margin: 12px 0;
  font-size: 14px;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

/* 数学符号样式 */
.chat-bubble .ai-message .math-symbol {
  font-family: 'Times New Roman', serif;
  font-weight: bold;
  color: #d32f2f;
  background: #fff3e0;
  padding: 2px 4px;
  border-radius: 4px;
  border: 1px solid #ffcc02;
  font-size: 1.1em;
  margin: 0 2px;
}

/* AI上下文提示样式 */
.ai-context-tip {
  display: flex;
  align-items: center;
  padding: 8px 16px;
  background: #f5f7fa;
  border-bottom: 1px solid #e4e7ed;
  margin-bottom: 16px;
  font-size: 14px;
  color: #606266;
}

.ai-context-tip i {
  margin-right: 8px;
  font-size: 16px;
}

.context-text {
  flex: 1;
  font-size: 14px;
  line-height: 1.6;
  color: #333;
}

.edit-context-btn {
  margin-left: 8px;
  flex-shrink: 0;
  color: #67c23a;
}

.edit-context-btn:hover {
  color: #85ce61;
}

/* 分隔线样式 */
.chat-bubble .ai-message hr {
  height: 2px;
  background: linear-gradient(90deg, transparent, #e0e0e0, transparent);
  border: none;
  margin: 24px 0;
}

/* 引用样式 */
.chat-bubble .ai-message blockquote {
  margin: 16px 0;
  padding: 12px 16px;
  color: #6a737d;
  border-left: 4px solid #dfe2e5;
  background: #f6f8fa;
  border-radius: 0 6px 6px 0;
  font-style: italic;
  word-wrap: break-word;
  overflow-wrap: break-word;
}

/* 保存提示（原来的写法） */
.saving-tip {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 13px;
  color: #67c23a;
  background: #f0f9eb;
  padding: 6px 12px;
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  z-index: 10;
}

/* 下载按钮样式 */
.header-right .el-button[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

.header-right .el-button[loading] {
  cursor: wait;
}

.header-right .el-button:not([disabled]):hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

/* 下载按钮加载动画 */
.header-right .el-button[loading] .el-icon-download {
  animation: download-spin 1s linear infinite;
}

@keyframes download-spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
</style>
